from django.template.loader import get_template
from django.template import RequestContext
from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.contrib import auth
from django.utils import simplejson as json

import os, GnuPGInterface, re, sys
from pgpauth.models import Sentence, SecureSentence, PublicKey
from random import randint
from settings import PROFILE_URL
from settings import DEFAULT_PASSWORD


class MyGnuPG(GnuPGInterface.GnuPG):
    def __init__(self):
        self.stdin = 'stdin'
        self.stdout = 'stdout'
        self.status = 'status'
        self.public_key_pattern = re.compile('([0-9a-fA-F]{40})')
        self.email_pattern = re.compile('<([\w\.+-]+@[\w\.-]+)>')

        GnuPGInterface.GnuPG.__init__(self)
        self.setup_my_options()

    def setup_my_options(self):
        self.options.armor = 1
        self.options.meta_interactive = 0
        self.options.extra_args.append('--no-secmem-warning')

    def getOutput(self, stdin=''):
        proc = self.process
        proc.handles[self.stdin].write(stdin)
        proc.handles[self.stdin].close()

        stdout = proc.handles[self.stdout].read()
        proc.handles[self.stdout].close()

        try:
            status = proc.handles[self.status].read()
            proc.handles[self.status].close()
        except:
            status = ''
        proc.wait()
        return {'stdout': stdout, 'status': status}

    def importKey(self, content):
        self.process = self.run(['--import'], create_fhs=[self.stdin, self.stdout, self.status])
        output = self.getOutput(stdin=content)
        result = self.public_key_pattern.search(output['status'])
        if not result:
            raise ImportError
        else:
            public_fingerprint = result.groups()[0]
            return public_fingerprint

    def getEmailFromListKey(self, fingerprint):
        self.process = self.run(['--list-keys', fingerprint], create_fhs=[self.stdin, self.stdout, self.status])
        output = self.getOutput()
        result = self.email_pattern.search(output['stdout'])
        if not result:
            raise ValueError
        else:
            email = result.groups()[0]
            return email

    def verifySignedText(self, content):
        self.process = self.run(['--verify', '--with-colons'], create_fhs=[self.stdin, self.stdout, self.status])
        output = self.getOutput(stdin=content)
        result = self.public_key_pattern.search(output['status'])
        if not result:
            raise ValueError
        else:
            public_fingerprint = result.groups()[0]
            return public_fingerprint

    def exportKey(self, fingerprint):
        self.process = self.run(['--export', fingerprint], create_fhs=[self.stdin, self.stdout, self.status])
        return self.getOutput()['stdout']

    def encryptText(self, content, recipients):
        self.options.recipients = recipients   # a list!
        self.process = self.run(['--encrypt'], create_fhs=[self.stdin, self.stdout])
        return self.getOutput(stdin=content)['stdout']

    def decryptText(self, content):
        self.process = self.run(['-d'], create_fhs=[self.stdin, self.stdout])
        return self.getOutput(stdin=content)['stdout']


def randomSentence():
    all_sentence = Sentence.objects.all()
    count = all_sentence.count()
    sentence = all_sentence[randint(0, count-1)]
    return sentence

def loginPage(R):
    if R.user.is_authenticated():
        return HttpResponseRedirect(PROFILE_URL)
    ss = SecureSentence(sentence=randomSentence())
    ss.save()
    t = get_template(os.path.join('pgpauth', 'index.html'))
    html = t.render(RequestContext(R, {'securesentence': ss}))
    return HttpResponse(html)

def loginByPGP(R):
    securesentence_id = R.POST.get('securesentence_id', None)
    signed_content = R.POST.get('signed_content', None)
    securesentence = SecureSentence(sentence=randomSentence())
    securesentence.save()

    if not securesentence_id  or not signed_content:
        return HttpResponse(json.dumps({'status': False,
            'securesentence_id': securesentence.id,
            'securesentence_content': securesentence.sentence.content,
            'message': 'the signed content is wrong!'}))

    try:
        ss = SecureSentence.objects.get(id=securesentence_id, used=False)
        ss.used = True
        ss.save()
        check_sentence = '%s.%s' % (ss.id, ss.sentence.content)
        if not check_sentence in signed_content:
            return HttpResponse(json.dumps({'status': False,
                'securesentence_id': securesentence.id,
                'securesentence_content': securesentence.sentence.content,
                'message': 'the BOLD message must in the signed message!'}))
    except SecureSentence.DoesNotExist:
        return HttpResponse(json.dumps({'status': False,
            'securesentence_id': securesentence.id,
            'securesentence_content': securesentence.sentence.content,
            'message': 'the login session was expired, please reload the login page!'}))

    mygpg = MyGnuPG()
    try:
        fingerprint = mygpg.verifySignedText(signed_content)
        publickey = PublicKey.objects.get(fingerprint=fingerprint)
    except ValueError:
        return HttpResponse(json.dumps({'status': False,
            'securesentence_id': securesentence.id,
            'securesentence_content': securesentence.sentence.content,
            'message': 'the signed content is wrong! we can find the public key'}))
    except:
        return HttpResponse(json.dumps({'status': False,
            'securesentence_id': securesentence.id,
            'securesentence_content': securesentence.sentence.content,
            'message': 'you did not uploaded public key yet'}))

    if not publickey.user:
        return HttpResponse(json.dumps({'status': False,
            'securesentence_id': securesentence.id,
            'securesentence_content': securesentence.sentence.content,
            'message': 'you did not create account by this public key yet'}))
    username = publickey.user.username
    user = auth.authenticate(username=username, password=DEFAULT_PASSWORD)
    auth.login(R, user)

    return HttpResponse(json.dumps({'status': True, 'next': PROFILE_URL}))

@login_required
def selectProfile(R):
    t = get_template(os.path.join('pgpauth', 'profile.html'))
    html = t.render(RequestContext(R, {}))
    return HttpResponse(html)


@login_required
def logout(R):
    auth.logout(R)
    return HttpResponseRedirect('/pgpauth/')

def selectUploadPublicKeyPage(R):
    if R.user.is_authenticated(): auth.logout(R)
    t = get_template(os.path.join('pgpauth', 'create_publickey.html'))
    html = t.render(RequestContext(R, {}))
    return HttpResponse(html)

def createPublicKey(R):
    file = R.FILES.get('file', None)
    if not file:
        return HttpResponse(json.dumps({'status': False,
            'message': 'there is no upload file'}))

    mygpg = MyGnuPG()
    fingerprint = mygpg.importKey(file.read())
    if not fingerprint:
        return HttpResponse(json.dumps({'status': False,
            'message': 'the public key file is non-standard'}))

    email = mygpg.getEmailFromListKey(fingerprint)
    publickey, is_created = PublicKey.objects.get_or_create(
        fingerprint=fingerprint, email=email)
    if not is_created:
        return HttpResponse(json.dumps({'status': False,
            'message': 'the public key file was uploaded'}))

    publickey.content = mygpg.exportKey(fingerprint)
    if not R.user.is_authenticated():
        publickey.save()
        return HttpResponse(json.dumps({'status': True,
            'id': publickey.id, 'email': publickey.email,
            'fingerprint': publickey.formatFingerprint()
        }))
    else:
        publickey.user = R.user
        publickey.save()
        return HttpResponse(json.dumps({'status': True, 'user': publickey.user.username,
            'id': publickey.id, 'email': publickey.email,
            'fingerprint': publickey.formatFingerprint()
        }))

def selectNewAccountPage(R, publickey_id):
    t = get_template(os.path.join('pgpauth', 'account_form.html'))
    try: publickey = PublicKey.objects.get(id=publickey_id)
    except PublicKey.DoesNotExist:
        html = t.render(RequestContext(R, {'message': 'you should upload public key first.'}))
    else:
        if publickey.user:
            html = t.render(RequestContext(R, {'message': 'you created the "username" before.'}))
        else:
            sentence = randomSentence()
            html = t.render(RequestContext(R, {'sentence': sentence}))
    return HttpResponse(html)

def createAccount(R):
    username = R.POST.get('username', None)
    signed_content = R.POST.get('signed_content', None)
    if not username or not signed_content:
        return HttpResponse(json.dumps({'status': False,
            'message': 'please type [username] and [signed_content]!'}))
    else:
        if User.objects.filter(username=username):
            return HttpResponse(json.dumps({'status': False,
                'message': 'the %s was created, please pick up other!' % username}))
        else:
            mygpg = MyGnuPG()
            fingerprint = None
            try:
                fingerprint = mygpg.verifySignedText(signed_content)
                publickey = PublicKey.objects.get(fingerprint=fingerprint)
            except ValueError:
                return HttpResponse(json.dumps({'status': False,
                    'message': 'the signed content is wrong! we can find the public key'}))
            except:
                if fingerprint:
                    return HttpResponse(json.dumps({'status': False,
                        'message': 'there is no public key(%s) on the system' % fingerprint}))
                else:
                    return HttpResponse(json.dumps({'status': False,
                        'message': 'the signed content is not verified'}))

            user = User(username=username, last_name='unset', first_name='unset', email=publickey.email)
            user.set_password(DEFAULT_PASSWORD)
            user.save()

            publickey.user = user
            publickey.save()

            user = auth.authenticate(username=username, password=DEFAULT_PASSWORD)
            auth.login(R, user)

            return HttpResponse(json.dumps({'status': True, 'next': PROFILE_URL}))

def selectAllPublicKey(R):
    t = get_template(os.path.join('pgpauth', 'publickey_list.html'))
    html = t.render(RequestContext(R, {'PublicKey': PublicKey}))
    return HttpResponse(html)


def createPublicKeyByAdd(R):
    pass
